package com.service.scheduling.task;


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.service.dao.constants.ConfigConstants;
import com.service.dao.enums.CoinsEnum;
import com.service.dao.enums.MemberJAType;
import com.service.dao.enums.StarGradeEnum;
import com.service.dao.model.*;
import com.service.dao.po.MemberCache;
import com.service.dao.util.StarGradeUtil;
import com.service.scheduling.dto.StoreReleaseCommonParam;
import com.service.scheduling.service.*;
import com.service.scheduling.support.MemberStoreLockUtils;
import com.service.scheduling.utils.MathUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.redisson.RedissonRedLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
@Component
public class MemberStoreReleaseTask {

    @Autowired
    private ConfigService configService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private MemberStoreService storeService;
    @Autowired
    private MemberStoreLockUtils storeLockUtils;
    @Autowired
    private MemberCacheService memberCacheService;
    @Autowired
    private MemberStoreReleaseService storeReleaseService;
    @Autowired
    private MemberStarGradeConfigService starGradeConfigService;
    @Autowired
    private RecommendAwardConfigService recommendAwardConfigService;

    private static BigDecimal ONE_HUNDRED = new BigDecimal(100);

    private String formatPattern = "YYYY-MM-dd hh:mm";

//    @Scheduled(cron = "0 0/5 * * * ?")
    public void doScheduled() {
        long startTime = System.currentTimeMillis();
        log.info("MemberStoreReleaseTask running start");
//        Map<String, SysConfig> configMap = configService.mapByConfigGroup(ConfigConstants.Group.merchant.getCode());
//        CoinsEnum releaseCoin = CoinsEnum.valueOf(configMap.get(ConfigConstants.OFFLINE_ORDER_RELEASE_COINS).getConfOptions());
//        CoinsEnum targetCoin = CoinsEnum.valueOf(configMap.get(ConfigConstants.OFFLINE_ORDER_RELEASE_TARGET_COINS).getConfOptions());
//        BigDecimal targetCoinPrice = getConcessionCoinPrice(configMap);
//        BigDecimal exchangeNum = MathUtil.parseBigDecimal(configMap.get(ConfigConstants.OFFLINE_ORDER_RELEASE_COINS_EXCHANGE_RATE).getConfValue());

//        BigDecimal releaseRatio = getRatio(configMap.get(ConfigConstants.OFFLINE_ORDER_RELEASE_RATIO).getConfValue());

        StoreReleaseCommonParam releaseCommonParam = StoreReleaseCommonParam.builder()
//                .releaseCoin(releaseCoin)
//                .targetCoin(targetCoin)
//                .releaseRation(releaseRatio)
//                .targetCoinPrice(targetCoinPrice)
//                .exchangeRate(exchangeNum)
                .build();

        releaseAllMemberStore(releaseCommonParam);
        log.info("MemberStoreReleaseTask running end,耗时:{}秒", (System.currentTimeMillis() - startTime) / 1000);
    }

    public void releaseAllMemberStore(StoreReleaseCommonParam releaseCommonParam) {


        Date currentDate = new Date();
        String currentDateStr = DateFormatUtils.format(currentDate, formatPattern);
        releaseCommonParam.setCurrentDateStr(currentDateStr);

        //自动释放以及根据推荐关系加速释放
        List<Member> memberList = memberService.listAll();
        List<RecommendAwardConfig> awardConfigList = recommendAwardConfigService.listByStatus();
        Map<Integer, MemberStarGradeConfig> starGradeConfigMap = starGradeConfigService.list2Map();
        Map<Integer, BigDecimal> todayReleaseMap = Maps.newConcurrentMap();

        memberList.parallelStream().forEach(item -> {
            BigDecimal releaseNumByItemRelease = calculateReleaseNumByAuto(item, releaseCommonParam);
            if (MathUtil.isLEZero(releaseNumByItemRelease)) {
                return;
            }
            todayReleaseMap.put(item.getId(), releaseNumByItemRelease);
        });

        
        memberList.forEach(item -> {
            if (!StarGradeEnum.getStarGrade().contains(item.getStarGrade())){
                return;
            }
            AtomicInteger count = new AtomicInteger(0);
            log.info("会员<id:{},loginAccount:{}>加速父级积分释放", item.getId(), item.getUserAccount());
            BigDecimal releaseNumByItemRelease = todayReleaseMap.getOrDefault(item.getId(), BigDecimal.ZERO);
            if (MathUtil.isLEZero(releaseNumByItemRelease)) {
                return;
            }
            AtomicReference<MemberStarGradeConfig> lastAwardGrade = new AtomicReference<>();
            lastAwardGrade.set(starGradeConfigMap.get(item.getStarGrade()));

            List<Member> memberListByPath = getReverseMemberPath(item.getUserPath());
            if (ObjectUtils.isEmpty(memberListByPath)) {
                return;
            }
            memberListByPath.forEach(memberItem -> {
                if (count.get() >= awardConfigList.size()) {
                    return;
                }
                //根据推荐关系以及推荐关系配置加速释放
                calculateAccelerateReleaseNumAndByRecommend(memberItem, item.getId(), releaseCommonParam,
                        awardConfigList.get(count.get()), getReleaseJaType(count.get()), releaseNumByItemRelease);
                count.getAndIncrement();

                //根据自身等级获取团队加速释放量
                calculateAccelerateReleaseNumAndByTeam(memberItem, item.getId(),
                        releaseCommonParam, starGradeConfigMap.get(memberItem.getStarGrade()),
                        lastAwardGrade, releaseNumByItemRelease);

            });
        });
    }

    private List<Integer> getAutoJaTypeList() {
        List<Integer> jaTypeList = Lists.newArrayList();
        jaTypeList.add(MemberJAType.RELEASE_BY_SELF.getCode());
        return jaTypeList;
    }

    /**
     * 自动释放积分
     *
     * @return
     */
    private BigDecimal calculateReleaseNumByAuto(Member member, StoreReleaseCommonParam param) {

        log.info("会员<id:{},loginAccount:{}>于{}释放{}产生{}", member.getId(), member.getUserAccount(), param.getCurrentDateStr(),
                param.getReleaseCoin().getName(), param.getTargetCoin().getName());

        BigDecimal releaseNumBySelf = releaseMemberStoreByReleaseRation(member, MemberJAType.RELEASE_BY_SELF, param, "会员自动释放");
        if (MathUtil.isLEZero(releaseNumBySelf)) {
            return releaseNumBySelf;
        }
        //修改会员等级
        memberService.checkAndUpdateMemberStarGrade(memberCacheService.getMemberCache(member.getId()));
        return releaseNumBySelf;
    }

    private void calculateAccelerateReleaseNumAndByRecommend(Member currentMember, Integer sourceMemberId, StoreReleaseCommonParam param
            , RecommendAwardConfig config, MemberJAType jaType, BigDecimal sourceMemberReleaseNum) {
        Integer number = memberService.countMemberByPidAndStarGrade(currentMember.getId(), StarGradeEnum.getStarGrade());
        if (number >= config.getRcDirectNum()) {
            String remark = currentMember.getId() + "->" + sourceMemberId + config.getAwardName() + "加速释放";
            BigDecimal releaseNum = sourceMemberReleaseNum.multiply(config.getReleaseMuRate());
            releaseMemberStoreByReleaseNum(currentMember, jaType, param, releaseNum, remark);
            memberService.checkAndUpdateMemberStarGrade(memberCacheService.getMemberCache(currentMember.getId()));
        }
    }


    private BigDecimal calculateAccelerateReleaseNumAndByTeam(Member currentMember, Integer sourceMemberId, StoreReleaseCommonParam param
            , MemberStarGradeConfig starGradeConfig, AtomicReference<MemberStarGradeConfig> lastStarGrade, BigDecimal sourceMemberReleaseNum) {

        if (MathUtil.isLEZero(sourceMemberReleaseNum)) {
            return BigDecimal.ZERO;
        }
        BigDecimal actualRate = !ObjectUtils.isEmpty(lastStarGrade.get()) ? starGradeConfig.getExpediteReleaseRate().subtract(lastStarGrade.get().getExpediteReleaseRate()) : starGradeConfig.getExpediteReleaseRate();
        if (MathUtil.isLEZero(actualRate)) {
            return BigDecimal.ZERO;
        }
        BigDecimal actualAccelerateNum = actualRate.multiply(sourceMemberReleaseNum);
        String remark = currentMember.getId() + "->" + sourceMemberId + "会员团队加速释放";
        BigDecimal releaseNum = releaseMemberStoreByReleaseNum(currentMember, MemberJAType.RELEASE_BY_TEAM, param, actualAccelerateNum, remark);
        if (MathUtil.isLEZero(releaseNum)) {
            return BigDecimal.ZERO;
        }

        lastStarGrade.getAndSet(starGradeConfig);
        memberService.checkAndUpdateMemberStarGrade(memberCacheService.getMemberCache(currentMember.getId()));
        return starGradeConfig.getExpediteReleaseRate();
    }


    private MemberJAType getReleaseJaType(Integer count) {
        return count == 0 ? MemberJAType.RELEASE_BY_RECOMMEND_11 : MemberJAType.RELEASE_BY_RECOMMEND_12;
    }


    public List<Member> getReverseMemberPath(String memberPath) {
        return Lists.reverse(memberService.getMemberByPath(memberPath));
    }


    private BigDecimal getConcessionCoinPrice(Map<String, SysConfig> configMap) {
        SysConfig config = configMap.get(ConfigConstants.OFFLINE_ORDER_CONCESSION_COIN_PRICE);
        return ObjectUtils.isEmpty(config) || StringUtils.isBlank(config.getConfValue()) ? BigDecimal.ZERO : MathUtil.parseBigDecimal(config.getConfValue());
    }

    /**
     * 根据释放比例释放
     *
     * @param member
     */
    public BigDecimal releaseMemberStoreByReleaseRation(Member member, MemberJAType memberJaType
            , StoreReleaseCommonParam param, String remark) {

        MemberStore releaseStore = storeService.getMemberStore(member.getId(), param.getReleaseCoin().getName());
        if (MathUtil.isLEZero(releaseStore.getNum())) {
            log.info("会员<id:{},loginAccount:{}>于{}{}{}产生{}:账户余额为0,未产生货币", member.getId(),
                    member.getUserAccount(), param.getCurrentDateStr(), param.getReleaseCoin().getName(), remark,
                    param.getTargetCoin().getName());
            return BigDecimal.ZERO;
        }
        BigDecimal releaseNum = MathUtil.mul(releaseStore.getNum(), param.getReleaseRation(), 8);
        releaseMemberStore(member, releaseStore, memberJaType, param, releaseNum, remark);
        return releaseNum;
    }


    /**
     * 根据释数量释放
     *
     * @param member
     * @param releaseNum
     */
    public BigDecimal releaseMemberStoreByReleaseNum(Member member, MemberJAType memberJaType
            , StoreReleaseCommonParam param, BigDecimal releaseNum, String remark) {

        MemberStore releaseStore = storeService.getMemberStore(member.getId(), param.getReleaseCoin().getName());
        if (MathUtil.isLEZero(releaseStore.getNum())) {
            log.info("会员<id:{},loginAccount:{}>于{}{}{}产生{}:账户余额为0,未产生货币", member.getId(),
                    member.getUserAccount(), param.getCurrentDateStr(), param.getReleaseCoin().getName(), remark,
                    param.getTargetCoin().getName());
            return BigDecimal.ZERO;
        }

        BigDecimal releaseActualNum = releaseStore.getNum().compareTo(releaseNum) >= 0 ? releaseNum : releaseStore.getNum();
        releaseMemberStore(member, releaseStore, memberJaType, param, releaseActualNum, remark);
        return releaseActualNum;
    }

    private void releaseMemberStore(Member member, MemberStore releaseStore, MemberJAType memberJaType, StoreReleaseCommonParam param, BigDecimal releaseNum, String remark) {
        RedissonRedLock redLock = getRewLock(releaseStore, param);
        try {
            if (storeLockUtils.redLockTryLock(redLock)) {
                storeReleaseService.releaseMemberStore(member, param, memberJaType, releaseNum, remark);
            }
        } finally {
            redLock.unlock();
        }
    }

    private RedissonRedLock getRewLock(MemberStore releaseStore, StoreReleaseCommonParam param) {
        List<MemberStore> storeList = Lists.newArrayList(
                releaseStore,
                storeService.getMemberStore(releaseStore.getMemberId(), param.getTargetCoin().getName())
        );
        List<String> storeLockKey = MemberStoreLockUtils.buildStoreKey2List(storeList);
        return storeLockUtils.getRedLock(storeLockKey);
    }


    private boolean isMemberGradeLessEqualThen(MemberCache memberCache, AtomicReference<MemberStarGradeConfig> lastGrade) {
        int memberRealGrade, compareRealGrade = 0;
        memberRealGrade = getRealGrade(memberCache.getStarGrade());
        compareRealGrade = getRealGrade(lastGrade.get().getId());
        return memberRealGrade <= compareRealGrade;
    }

    private int getRealGrade(Integer gradeOne) {
        return StarGradeUtil.isAdminModifyGrade(gradeOne) ? gradeOne - 100 : gradeOne;
    }

    public BigDecimal getRatio(String confValue) {
        if (StringUtils.isBlank(confValue)) {
            return BigDecimal.ZERO;
        }
        return MathUtil.div(MathUtil.parseBigDecimal(confValue), ONE_HUNDRED, 4);
    }

    public BigDecimal getRatio(BigDecimal rate) {
        if (MathUtil.isLEZero(rate)) {
            return BigDecimal.ZERO;
        }
        return MathUtil.div(rate, ONE_HUNDRED, 4);
    }


}
